

from flask import json
from flask_sqlalchemy import BaseQuery
from flask_sqlalchemy import Model as _Model
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import inspect, orm
from sqlalchemy.sql.functions import _FunctionGenerator




class MixinJSONSerializer:
    def __init__(self):
        self._exclude = None
        self._fields = None

    @orm.reconstructor
    def init_on_load(self):
        self._fields = []
        self._exclude = []

        self._set_fields()
        self.__prune_fields()

    def _set_fields(self):
        pass

    def __prune_fields(self):
        columns = inspect(self.__class__).columns
        if not self._fields:
            all_columns = set([column.name for column in columns])
            self._fields = list(all_columns - set(self._exclude))

    def hide(self, *args):
        for key in args:
            self._fields.remove(key)
        return self

    def keys(self):
        if not hasattr(self, "_fields"):
            self.init_on_load()
        return self._fields

    def __getitem__(self, key):
        return getattr(self, key)


class Query(BaseQuery):
    def filter_by(self, soft=False, **kwargs):
        # soft 应用软删除
        if soft:
            kwargs["delete_time"] = None
        return super(Query, self).filter_by(**kwargs)


class Model(_Model):
    def __repr__(self):
        detail = json.dumps(
            {k: v for k, v in self.__dict__.items() if not k.startswith("_")}
        )
        identity = inspect(self).identity
        if identity is None:
            pk = "\n(transient {0} {1})\n".format(id(self), detail)
        else:
            pk = ",".join(to_str(value) for value in identity)
        return "\n<{0} {1} {2}>\n".format(type(self).__name__, pk, detail)


def to_str(x, charset="utf8", errors="strict"):
    if x is None or isinstance(x, str):
        return x

    if isinstance(x, bytes):
        return x.decode(charset, errors)

    return str(x)


func = _FunctionGenerator()
func.__doc__ = _FunctionGenerator.__doc__

db = SQLAlchemy(query_class=Query, model_class=Model)
